GIS in R

In this exercise I will be mapping the current distribution of three species of protea chafer that occur in the Western Cape, to determine if their current ranges overlap with endangered ecosystem types (using iNat and SANBI/CapeNature data). I aim to create an interactive map using mapview() that allows viewers to pan, zoom and inspect individual observations within the City of Cape Town region only, the ecosystem threat status that they occur in, as well as the name of the ecosystem the occur in.

Basemaps and Data

Threatened ecosystem types sourced from SANBI/CapeNature: https://bgis.sanbi.org/SpatialDataset/Detail/611. These data map the current threat statuses for various ecosystems in the Western Cape, and the shape files will be cropped to map only the City of Cape Town area.

Species observation data sourced from iNaturalist: https://www.inaturalist.org/observations?place_id=6986&subview=map&taxon_id=423700&verifiable=any&view=species&iconic_taxa=Insecta. These data include research grade, wild observations (though some observations occur within Kirstenbosch Botanical Gardens, and thus may be considered ‘captive’). Sub-species are included in the broader species classification for ease, and the mapped distributions will be confined to the City of Cape Town area.

Loading Data files into R:

Threatened ecosystems in the Western Cape

Shape files of ecosystem statuses were sourced from the 2016 Ecosystem Threat Status project conducted by SANBI and CapeNature (https://bgis.sanbi.org/SpatialDataset/Detail/611). A 2018 version does exist, but the shapefile is too large to use in this exercise.

library(sf) # load the sf library - this allows us to work with shape files.
## Linking to GEOS 3.13.0, GDAL 3.10.1, PROJ 9.5.1; sf_use_s2() is TRUE
library(tidyverse) # load the tidyverse library - this allows us to use all the tidyverse tools 
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# read the shapefile into R
threat_eco <- st_read("data/WCBSP_Ecosystem_Threat_Status_2016/BSP_Ecosystem_Threat_Status_2016.shp")
## Reading layer `BSP_Ecosystem_Threat_Status_2016' from data source 
##   `C:\Users\Arwenn Kummer\Documents\GIT\GISinR\data\WCBSP_Ecosystem_Threat_Status_2016\BSP_Ecosystem_Threat_Status_2016.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 174 features and 7 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 17.75735 ymin: -34.8334 xmax: 24.22241 ymax: -30.43026
## Geodetic CRS:  WGS 84
# determine the coordinate reference system
st_crs(threat_eco)
## Coordinate Reference System:
##   User input: WGS 84 
##   wkt:
## GEOGCRS["WGS 84",
##     DATUM["World Geodetic System 1984",
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["latitude",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["longitude",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     ID["EPSG",4326]]
# plot threat_eco in ggplot() to do a visual check. The `CNa1_ETS14` variable gives us the threat status of each ecosystem
ggplot() +
  geom_sf(data = threat_eco, aes(fill = `CNa1_ETS14`)) +
  theme_minimal()

This maps the ecosystem status for the whole of the Western Cape. We want to only focus on the Cape Peninsula and the City of Cape Town. Thus we will crop the map:

# check for invalid geometries before cropping
threat_eco_valid <- threat_eco[st_is_valid(threat_eco), ]

# Define the bounding box for the City of Cape Town
myextent <- st_bbox(c(xmin = 18, ymin = -34.5, xmax = 19, ymax = -33), crs = st_crs(threat_eco))

# Crop the map
threat_eco_cropped <- st_crop(threat_eco_valid, myextent)
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries

There were multiple errors when trying to generate the map, stating that there were overlapping polygons, or areas sharing the same vertex. The above code works around this, but it is possible that some ecosystem information may be lost in the final map…this not ideal at all, but at least the code runs.

Observational Data from iNaturalist

Three of the most common protea chafers found in the Western Cape will be used in this distribution mapping exercise; Cape Protea Chafer (Trichostetha capensis), Green Protea Chafer (T. fascicularis) and Signal Protea Chafer (T. signata). These will be read into R using a function from rinat(): get_inat_obs(), and searches will be confined to the City of Cape Town by setting the bounds as (-34.5, 18, -33, 19). Each species’ observations will be stored in a data frame and subsequently converted to a shape-file for mapping.

Cape Protea Chafer:

# load the rinat() library - allows us to read data from iNaturalist directly
library(rinat)

# load observations
cpc <- get_inat_obs(taxon_name = "Trichostetha capensis",
                   bounds = c(-34.5, 18, -33, 19), # confined searches to the City of Cape Town
                   maxresults = 400)
head(cpc)
##                  scientific_name                  datetime description
## 1 Trichostetha capensis capensis 2018-10-10 08:18:00 +0200            
## 2 Trichostetha capensis capensis 2024-12-10 12:22:00 +0200            
## 3 Trichostetha capensis capensis 2024-12-01 10:22:52 +0200            
## 4          Trichostetha capensis 2024-12-11 09:45:15 +0200            
## 5 Trichostetha capensis capensis 2024-12-01 12:10:00 +0200            
## 6          Trichostetha capensis 2024-10-19 11:55:27 +0200            
##                                                 place_guess  latitude longitude
## 1                     Тейбл Маунтин, Кейптаун, Южная Африка -33.95552  18.39700
## 2  Table Mountain (Nature Reserve), Cape Town, South Africa -33.95515  18.39215
## 3         Table Mountain National Park, ZA-WC-CT, ZA-WC, ZA -34.22127  18.40226
## 4 Table Mountain National Park, Cape Town, Western Cape, ZA -34.10117  18.43614
## 5  Table Mountain (Nature Reserve), Cape Town, South Africa -34.00394  18.38284
## 6    Paarl Mountain Nature Reserve, Paarl, Western Cape, ZA -33.73462  18.94322
##   tag_list                  common_name
## 1          Peninsula Cape Protea Chafer
## 2          Peninsula Cape Protea Chafer
## 3          Peninsula Cape Protea Chafer
## 4                    Cape Protea Chafer
## 5          Peninsula Cape Protea Chafer
## 6                    Cape Protea Chafer
##                                                  url
## 1 https://www.inaturalist.org/observations/262478970
## 2 https://www.inaturalist.org/observations/258090301
## 3 https://www.inaturalist.org/observations/255569176
## 4 https://www.inaturalist.org/observations/254806568
## 5 https://www.inaturalist.org/observations/254531470
## 6 https://www.inaturalist.org/observations/253325744
##                                                                     image_url
## 1 https://inaturalist-open-data.s3.amazonaws.com/photos/471516246/medium.jpeg
## 2  https://inaturalist-open-data.s3.amazonaws.com/photos/463003326/medium.jpg
## 3 https://inaturalist-open-data.s3.amazonaws.com/photos/458021136/medium.jpeg
## 4  https://inaturalist-open-data.s3.amazonaws.com/photos/456491316/medium.jpg
## 5  https://inaturalist-open-data.s3.amazonaws.com/photos/455930354/medium.jpg
## 6 https://inaturalist-open-data.s3.amazonaws.com/photos/453565970/medium.jpeg
##        user_login        id                  species_guess iconic_taxon_name
## 1   alexander1951 262478970 Trichostetha capensis capensis           Insecta
## 2 wetlandwanderer 258090301   Peninsula Cape Protea Chafer           Insecta
## 3         lmossop 255569176 Trichostetha capensis capensis           Insecta
## 4   lindalakeside 254806568                                          Insecta
## 5    oswaldkurten 254531470   Peninsula Cape Protea Chafer           Insecta
## 6    dylan_vdmast 253325744             Cape Protea Chafer           Insecta
##   taxon_id num_identification_agreements num_identification_disagreements
## 1   694152                             1                                0
## 2   694152                             2                                0
## 3   694152                             2                                0
## 4   605066                             0                                0
## 5   694152                             2                                0
## 6   605066                             0                                0
##          observed_on_string observed_on        time_observed_at time_zone
## 1          2018/10/10 08:18  2018-10-10 2018-10-10 06:18:00 UTC  Pretoria
## 2       2024/12/10 12:22 PM  2024-12-10 2024-12-10 10:22:00 UTC  Pretoria
## 3       2024-12-01 10:22:52  2024-12-01 2024-12-01 08:22:52 UTC  Pretoria
## 4 2024-12-11 09:45:15+02:00  2024-12-11 2024-12-11 07:45:15 UTC  Pretoria
## 5       2024/12/01 12:10 PM  2024-12-01 2024-12-01 10:10:00 UTC  Pretoria
## 6 2024-10-19 11:55:27+02:00  2024-10-19 2024-10-19 09:55:27 UTC  Pretoria
##   positional_accuracy public_positional_accuracy geoprivacy taxon_geoprivacy
## 1                 244                        244                          NA
## 2                   4                          4                          NA
## 3                   6                          6                          NA
## 4                   8                          8                          NA
## 5                   5                          5                          NA
## 6                   4                          4                          NA
##   coordinates_obscured positioning_method positioning_device user_id
## 1                false                                       1417418
## 2                false                                       4808041
## 3                false                                        748780
## 4                false                                       1474346
## 5                false                                       1154120
## 6                false                                       3769361
##        user_name              created_at              updated_at quality_grade
## 1                2025-02-21 05:19:53 UTC 2025-02-27 13:41:39 UTC      research
## 2                2025-01-11 02:39:34 UTC 2025-01-11 04:28:09 UTC      research
## 3 Leighan Mossop 2024-12-19 08:08:13 UTC 2024-12-20 15:59:48 UTC      research
## 4   Linda Hibbin 2024-12-11 13:00:54 UTC 2025-01-12 19:25:51 UTC      needs_id
## 5         Oswald 2024-12-08 17:59:54 UTC 2025-01-17 18:56:03 UTC      research
## 6                2024-11-27 20:52:56 UTC 2024-11-28 14:16:39 UTC      research
##    license sound_url oauth_application_id captive_cultivated
## 1 CC-BY-NC        NA                   NA              false
## 2 CC-BY-NC        NA                   NA              false
## 3 CC-BY-NC        NA                    2              false
## 4 CC-BY-NC        NA                    3              false
## 5 CC-BY-NC        NA                   NA              false
## 6 CC-BY-NC        NA                    3              false
# filter for only research grade and wild observations
cpc <- cpc %>% filter(positional_accuracy<46 & 
                      latitude<0 &
                      !is.na(latitude) &
                      captive_cultivated == "false" &
                      quality_grade == "research")
# check the variable class 
class(cpc) # it's a data frame, and we need a shapefile
## [1] "data.frame"
# convert the data frame to a spatial object to allow mapping
cpc_sf <- st_as_sf(cpc, coords = c("longitude", "latitude"), crs = 4326)

# check the coordinate reference system and class
st_crs(cpc_sf) ; class(cpc_sf)
## Coordinate Reference System:
##   User input: EPSG:4326 
##   wkt:
## GEOGCRS["WGS 84",
##     ENSEMBLE["World Geodetic System 1984 ensemble",
##         MEMBER["World Geodetic System 1984 (Transit)"],
##         MEMBER["World Geodetic System 1984 (G730)"],
##         MEMBER["World Geodetic System 1984 (G873)"],
##         MEMBER["World Geodetic System 1984 (G1150)"],
##         MEMBER["World Geodetic System 1984 (G1674)"],
##         MEMBER["World Geodetic System 1984 (G1762)"],
##         MEMBER["World Geodetic System 1984 (G2139)"],
##         MEMBER["World Geodetic System 1984 (G2296)"],
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ENSEMBLEACCURACY[2.0]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Horizontal component of 3D system."],
##         AREA["World."],
##         BBOX[-90,-180,90,180]],
##     ID["EPSG",4326]]
## [1] "sf"         "data.frame"
 # plot the shape file to check that everything works and runs
ggplot() +
  geom_sf(data = cpc_sf[1]) +
  theme(legend.key.width = unit(10.1, "cm")) 

Green Protea Chafer:

# load observations
gpc <- get_inat_obs(taxon_name = "Trichostetha fascicularis",
                    bounds = c(-34.5, 18, -33, 19), # confined searches to the City of Cape Town
                    maxresults = 300)

head(gpc)
##                          scientific_name                  datetime description
## 1 Trichostetha fascicularis fascicularis 2024-10-04 13:03:56 +0200            
## 2 Trichostetha fascicularis fascicularis 2024-11-12 08:05:23 +0200            
## 3 Trichostetha fascicularis fascicularis 2024-10-16 17:08:00 +0200            
## 4 Trichostetha fascicularis fascicularis 2024-10-13 12:44:57 +0200            
## 5 Trichostetha fascicularis fascicularis 2024-10-11 14:17:00 +0200            
## 6              Trichostetha fascicularis 2024-09-15 12:42:23 +0200            
##                                                    place_guess  latitude
## 1                                             Western Cape, ZA -34.05064
## 2                   Meadowridge, Cape Town, 7806, South Africa -34.03278
## 3                Wynberg NU (2), Cape Town, 7824, South Africa -33.99025
## 4                         Nautilus Street, Betty's Bay, WC, ZA -34.36043
## 5 90 de Villiers Way, Glencairn, Cape Town, 7975, South Africa -34.15540
## 6            Table Mountain National Park, ZA-WC-CT, ZA-WC, ZA -33.99267
##   longitude tag_list              common_name
## 1  18.52667          Cape Green Protea Chafer
## 2  18.45004          Cape Green Protea Chafer
## 3  18.42909          Cape Green Protea Chafer
## 4  18.95556          Cape Green Protea Chafer
## 5  18.41427          Cape Green Protea Chafer
## 6  18.42698               Green Protea Chafer
##                                                  url
## 1 https://www.inaturalist.org/observations/256232032
## 2 https://www.inaturalist.org/observations/251472279
## 3 https://www.inaturalist.org/observations/247727495
## 4 https://www.inaturalist.org/observations/247093852
## 5 https://www.inaturalist.org/observations/246830608
## 6 https://www.inaturalist.org/observations/245037833
##                                                                     image_url
## 1                  https://static.inaturalist.org/photos/459359094/medium.jpg
## 2 https://inaturalist-open-data.s3.amazonaws.com/photos/449964134/medium.jpeg
## 3  https://inaturalist-open-data.s3.amazonaws.com/photos/442508752/medium.jpg
## 4 https://inaturalist-open-data.s3.amazonaws.com/photos/441243640/medium.jpeg
## 5 https://inaturalist-open-data.s3.amazonaws.com/photos/440722853/medium.jpeg
## 6 https://inaturalist-open-data.s3.amazonaws.com/photos/437191752/medium.jpeg
##       user_login        id            species_guess iconic_taxon_name taxon_id
## 1      danegreen 256232032 Cape Green Protea Chafer           Insecta   607308
## 2  angelamcqueen 251472279 Cape Green Protea Chafer           Insecta   607308
## 3         jrgale 247727495 Cape Green Protea Chafer           Insecta   607308
## 4       csrimage 247093852 Cape Green Protea Chafer           Insecta   607308
## 5      debtherat 246830608 Cape Green Protea Chafer           Insecta   607308
## 6 migsgreenworld 245037833      Green Protea Chafer           Insecta   423694
##   num_identification_agreements num_identification_disagreements
## 1                             2                                0
## 2                             2                                0
## 3                             2                                0
## 4                             2                                0
## 5                             2                                0
## 6                             1                                0
##          observed_on_string observed_on        time_observed_at time_zone
## 1 2024-10-04 13:03:56+02:00  2024-10-04 2024-10-04 11:03:56 UTC  Pretoria
## 2       2024-11-12 08:05:23  2024-11-12 2024-11-12 06:05:23 UTC  Pretoria
## 3        2024/10/16 5:08 PM  2024-10-16 2024-10-16 15:08:00 UTC  Pretoria
## 4 2024-10-13 12:44:57+02:00  2024-10-13 2024-10-13 10:44:57 UTC  Pretoria
## 5        2024/10/11 2:17 PM  2024-10-11 2024-10-11 12:17:00 UTC  Pretoria
## 6 2024-09-15 12:42:23+02:00  2024-09-15 2024-09-15 10:42:23 UTC  Pretoria
##   positional_accuracy public_positional_accuracy geoprivacy taxon_geoprivacy
## 1                   5                      28874   obscured               NA
## 2                1187                       1187                          NA
## 3                   4                          4                          NA
## 4                  48                         48                          NA
## 5                  54                         54                          NA
## 6                   9                          9                          NA
##   coordinates_obscured positioning_method positioning_device user_id user_name
## 1                 true                                       8845744          
## 2                false                gps                gps 1605299          
## 3                false                                       3762511 John Gale
## 4                false                                       5149013          
## 5                false                                        852523       deb
## 6                false                                       4988081          
##                created_at              updated_at quality_grade  license
## 1 2024-12-26 03:22:44 UTC 2024-12-26 17:26:00 UTC      research         
## 2 2024-11-12 19:41:24 UTC 2024-11-13 07:03:50 UTC      research CC-BY-NC
## 3 2024-10-17 06:05:00 UTC 2024-10-17 17:19:22 UTC      research CC-BY-NC
## 4 2024-10-13 10:52:00 UTC 2024-10-13 13:25:42 UTC      research CC-BY-NC
## 5 2024-10-11 21:09:56 UTC 2024-10-12 14:11:04 UTC      research CC-BY-NC
## 6 2024-10-01 08:17:09 UTC 2024-10-15 17:50:02 UTC      research    CC-BY
##   sound_url oauth_application_id captive_cultivated
## 1        NA                    3              false
## 2        NA                    2              false
## 3        NA                   NA              false
## 4        NA                    3              false
## 5        NA                   NA              false
## 6        NA                    3              false
# filter for research grade and wild observations
gpc <- gpc %>% filter(positional_accuracy<46 & 
                      latitude<0 &
                      !is.na(latitude) &
                      captive_cultivated == "false" &
                      quality_grade == "research")
# check the variable class
class(gpc) # it's a data frame. We need a shape file.
## [1] "data.frame"
# convert the data frame to a spatial object
gpc_sf <- st_as_sf(gpc, coords = c("longitude", "latitude"), crs = 4326)

# check the coordinate reference system and class
st_crs(gpc_sf) ; class(gpc_sf)
## Coordinate Reference System:
##   User input: EPSG:4326 
##   wkt:
## GEOGCRS["WGS 84",
##     ENSEMBLE["World Geodetic System 1984 ensemble",
##         MEMBER["World Geodetic System 1984 (Transit)"],
##         MEMBER["World Geodetic System 1984 (G730)"],
##         MEMBER["World Geodetic System 1984 (G873)"],
##         MEMBER["World Geodetic System 1984 (G1150)"],
##         MEMBER["World Geodetic System 1984 (G1674)"],
##         MEMBER["World Geodetic System 1984 (G1762)"],
##         MEMBER["World Geodetic System 1984 (G2139)"],
##         MEMBER["World Geodetic System 1984 (G2296)"],
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ENSEMBLEACCURACY[2.0]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Horizontal component of 3D system."],
##         AREA["World."],
##         BBOX[-90,-180,90,180]],
##     ID["EPSG",4326]]
## [1] "sf"         "data.frame"
# plot the shape file to check that everything works and runs
ggplot() +
  geom_sf(data = gpc_sf[1]) +
  theme(legend.key.width = unit(10.1, "cm")) 

Signal Protea Chafer:

# load observations
spc <- get_inat_obs(taxon_name = "Trichostetha signata",
                    bounds = c(-34.5, 18, -33, 19), # confined searches to the City of Cape Town
                    maxresults = 120)
head(spc)
##                 scientific_name                  datetime description
## 1 Trichostetha signata tibialis 2024-11-27 10:19:00 +0200            
## 2          Trichostetha signata 2024-09-24 12:10:49 +0200            
## 3          Trichostetha signata 2023-11-25 11:42:00 +0200            
## 4  Trichostetha signata signata 2023-11-27 18:51:09 +0200            
## 5  Trichostetha signata signata 2023-11-27 18:51:09 +0200            
## 6          Trichostetha signata 2023-11-26 12:36:00 +0200            
##                                              place_guess  latitude longitude
## 1           Overberg District Municipality, South Africa -34.16741  18.97198
## 2                Stellenbosch Municipality, South Africa -33.97647  18.89099
## 3           Overberg District Municipality, South Africa -34.33027  18.85016
## 4                                    Overberg, ZA-WC, ZA -34.33643  18.84035
## 5                                    Overberg, ZA-WC, ZA -34.33643  18.84035
## 6 Helderberg Rural, Sir Lowry's Pass, 7135, South Africa -34.18278  18.94465
##                  tag_list                    common_name
## 1 CREW HH 241127 Rockview Peninsula Signal Protea Chafer
## 2                                   Signal Protea Chafer
## 3                                   Signal Protea Chafer
## 4                            Common Signal Protea Chafer
## 5                            Common Signal Protea Chafer
## 6    GSB 231126 Steenbras           Signal Protea Chafer
##                                                  url
## 1 https://www.inaturalist.org/observations/260726074
## 2 https://www.inaturalist.org/observations/243816266
## 3 https://www.inaturalist.org/observations/192975617
## 4 https://www.inaturalist.org/observations/192371586
## 5 https://www.inaturalist.org/observations/192368589
## 6 https://www.inaturalist.org/observations/192328847
##                                                                     image_url
## 1 https://inaturalist-open-data.s3.amazonaws.com/photos/468270776/medium.jpeg
## 2                 https://static.inaturalist.org/photos/434814045/medium.jpeg
## 3  https://inaturalist-open-data.s3.amazonaws.com/photos/338884563/medium.jpg
## 4 https://inaturalist-open-data.s3.amazonaws.com/photos/337687168/medium.jpeg
## 5 https://inaturalist-open-data.s3.amazonaws.com/photos/337687099/medium.jpeg
## 6  https://inaturalist-open-data.s3.amazonaws.com/photos/337604344/medium.jpg
##      user_login        id                  species_guess iconic_taxon_name
## 1        linkie 260726074 Peninsula Signal Protea Chafer           Insecta
## 2  wikus_burger 243816266           Signal Protea Chafer           Insecta
## 3       adele84 192975617           Signal Protea Chafer           Insecta
## 4       adele84 192371586    Common Signal Protea Chafer           Insecta
## 5       adele84 192368589    Common Signal Protea Chafer           Insecta
## 6 carinalochner 192328847           Signal Protea Chafer           Insecta
##   taxon_id num_identification_agreements num_identification_disagreements
## 1   694164                             1                                0
## 2   633155                             1                                0
## 3   633155                             3                                1
## 4   694163                             3                                0
## 5   694163                             3                                0
## 6   633155                             2                                0
##    observed_on_string observed_on        time_observed_at time_zone
## 1 2024/11/27 10:19 AM  2024-11-27 2024-11-27 08:19:00 UTC  Pretoria
## 2 2024-09-24 12:10:49  2024-09-24 2024-09-24 10:10:49 UTC  Pretoria
## 3 2023/11/25 11:42 AM  2023-11-25 2023-11-25 09:42:00 UTC  Pretoria
## 4 2023-11-27 18:51:09  2023-11-27 2023-11-27 16:51:09 UTC  Pretoria
## 5 2023-11-27 18:51:09  2023-11-27 2023-11-27 16:51:09 UTC  Pretoria
## 6 2023/11/26 12:36 PM  2023-11-26 2023-11-26 10:36:00 UTC  Pretoria
##   positional_accuracy public_positional_accuracy geoprivacy taxon_geoprivacy
## 1                   8                          8         NA               NA
## 2                  NA                         NA         NA               NA
## 3                   3                          3         NA               NA
## 4                   4                          4         NA               NA
## 5                   4                          4         NA               NA
## 6                   8                          8         NA               NA
##   coordinates_obscured positioning_method positioning_device user_id
## 1                false                                        681342
## 2                false                                       4569056
## 3                false                                       4939503
## 4                false                                       4939503
## 5                false                                       4939503
## 6                false                                        724582
##                   user_name              created_at              updated_at
## 1                           2025-02-05 18:01:51 UTC 2025-02-06 07:46:12 UTC
## 2              Wikus Burger 2024-09-24 16:31:03 UTC 2024-09-25 05:47:57 UTC
## 3 Adele Scheepers Lamprecht 2023-12-03 22:43:01 UTC 2024-04-18 20:56:52 UTC
## 4 Adele Scheepers Lamprecht 2023-11-27 20:06:57 UTC 2023-12-03 23:19:33 UTC
## 5 Adele Scheepers Lamprecht 2023-11-27 19:40:06 UTC 2024-01-07 14:47:47 UTC
## 6            Carina Lochner 2023-11-27 12:38:41 UTC 2023-12-02 06:53:12 UTC
##   quality_grade  license sound_url oauth_application_id captive_cultivated
## 1      research CC-BY-NC        NA                   NA              false
## 2      research                 NA                    2              false
## 3      research    CC-BY        NA                   NA              false
## 4      research    CC-BY        NA                    2              false
## 5      research    CC-BY        NA                    2              false
## 6      research CC-BY-NC        NA                   NA              false
# select only research grade and wild observations
spc <- spc %>% filter(positional_accuracy<46 & 
                      latitude<0 &
                      !is.na(latitude) &
                      captive_cultivated == "false" &
                      quality_grade == "research")
# check the variable class
class(spc) # it's a data frame and we need a shape file
## [1] "data.frame"
# convert the data frame to a spatial object
spc_sf <- st_as_sf(spc, coords = c("longitude", "latitude"), crs = 4326)

# check the coordinate reference system and class
st_crs(spc_sf) ; class(spc_sf)
## Coordinate Reference System:
##   User input: EPSG:4326 
##   wkt:
## GEOGCRS["WGS 84",
##     ENSEMBLE["World Geodetic System 1984 ensemble",
##         MEMBER["World Geodetic System 1984 (Transit)"],
##         MEMBER["World Geodetic System 1984 (G730)"],
##         MEMBER["World Geodetic System 1984 (G873)"],
##         MEMBER["World Geodetic System 1984 (G1150)"],
##         MEMBER["World Geodetic System 1984 (G1674)"],
##         MEMBER["World Geodetic System 1984 (G1762)"],
##         MEMBER["World Geodetic System 1984 (G2139)"],
##         MEMBER["World Geodetic System 1984 (G2296)"],
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ENSEMBLEACCURACY[2.0]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Horizontal component of 3D system."],
##         AREA["World."],
##         BBOX[-90,-180,90,180]],
##     ID["EPSG",4326]]
## [1] "sf"         "data.frame"
# plot the shape file to check that everything works and runs
ggplot() +
  geom_sf(data = spc_sf[1]) +
  theme(legend.key.width = unit(10.1, "cm"))  

A note on subspecies:

There are multiple subspecies for each of the above species. For simplicity, these subspecies observations have been included within the mapping of the main species. i.e. there is no differentiation between subspecies of the same species within this mapping exercise.

Coordinate Reference Systems…

Fortunately, all of the shape file coordinate reference systems are WGS84 - World Geodesic System 1984. This means we don’t need to transform any of the layers before we can overlap them. We can also see from generating the above plots, that the data for threatened ecosystems and each of the species plots appropriately on coordinate axes. Now we can start layering them and working on an interactive map.

Mapping

Beetle distribution and threatened ecosystems in the Western Cape

First, we’ll create a static map that shows the ecosystem threat-status, with the beetles distribution shape files overlaid. We’ll do this using ggplot() for now.

# Let's make the map a bit prettier: plotting the ecosystem threat status with a different colour scheme and correcting the legend title.
ggplot() +
  geom_sf(data = threat_eco_cropped, aes(fill = `CNa1_ETS14`)) +
  scale_fill_manual(values = c("#ffac27", "#e7815d", "#ce5693", "#b62bc9", "#9d00ff")) +
  labs(fill = "Ecosystem threat status") +
  theme_minimal()

Now, I scoured Google and Stackexchange trying to find out how to add a legend for the beetles but couldn’t find a solution that ran and generated a legend. As such the legend is here in plain text: T. capensis = pink. T. fascicularis = green. T. signata = turquoise.

# Overlap the beetles' distributions
 ggplot() +
  geom_sf(data = threat_eco_cropped, aes(fill =  `CNa1_ETS14`)) +
  scale_fill_manual(values = c("#ffac27", "#e7815d", "#ce5693", "#b62bc9", "#9d00ff")) +
  labs(fill = "Ecosystem threat status") +
  geom_sf(data = cpc_sf, color = "pink", size =0.8) +
  geom_sf(data = gpc_sf, color = "green", size = 0.8) +
  geom_sf(data = spc_sf, color = "turquoise", size = 0.8) +
  theme_minimal()

This generates a good static map, and provides us with a broad scale view of the species distributions across the various ecosystems. But what if we want to be able to look closer and see exactly where these species occur, and in what ecosystem type? To achieve this, we will generate an interactive map using this same data.

Interactive Map

library(mapview) # allows us to create an interactive map
library(RColorBrewer) # loads in a palette of colour schemes

mapview(threat_eco_cropped, zcol = c("CNa1_ETS14"), col.regions = brewer.pal(5,"RdYlBu"), map.types = "CartoDB.Positron", layer.name = c("Ecosystem Threat Status")) + 
  mapview(threat_eco_cropped, zcol = c("NAME"), layer.name = c("Ecosystem name"), hide = TRUE) +
  mapview(cpc_sf, col.regions = "hotpink", layer.name = c("Trichostetha capensis")) + 
  mapview(gpc_sf, col.regions = "green", layer.name = c("Trichostetha fascicularis")) + 
  mapview(spc_sf, col.regions = "turquoise", layer.name = c("Trichostetha signata"))

With this interactive map, we are able to visually assess the current distribution of the protea chafer species, which ecosystem types they are found in, and the ecosystem’s threat status. From this, we can see that most fall within a variety of different ecosystems, with the most common being least threatened. Yay! This method of mapping species distributions could be applied to other species at greater risk, and can be used to inform conservation efforts and determine which species (and subspecies) may be most threatened by habitat loss and their ecosystems’ threat status.